package com.pig4cloud.pigx.contract.api.v1.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.customize.api.enums.AbnormalEnum;
import com.pig4cloud.pigx.common.customize.api.util.OpenApiUtil;
import com.pig4cloud.pigx.common.customize.enums.CommonDictEnum;
import com.pig4cloud.pigx.common.customize.i18n.util.MessageUtils;
import com.pig4cloud.pigx.common.customize.security.utils.SecurityUtil;
import com.pig4cloud.pigx.common.security.service.PigxUser;
import com.pig4cloud.pigx.contract.api.v1.service.OrderServiceV1;
import com.pig4cloud.pigx.contract.api.v1.utils.OpenApiContractUtilsV1;
import com.pig4cloud.pigx.contract.constant.I18nMessages;
import com.pig4cloud.pigx.contract.dto.ContractOrderCancelDTO;
import com.pig4cloud.pigx.contract.dto.ContractOrderCloseByPositionDTO;
import com.pig4cloud.pigx.contract.dto.ContractOrderCloseDTO;
import com.pig4cloud.pigx.contract.dto.ContractOrderCloseMarketDTO;
import com.pig4cloud.pigx.contract.dto.ContractOrderNewDTO;
import com.pig4cloud.pigx.contract.dto.ContractOrderOpenDTO;
import com.pig4cloud.pigx.contract.entity.ContractOrder;
import com.pig4cloud.pigx.contract.entity.ContractOrderHistory;
import com.pig4cloud.pigx.contract.entity.ContractPosition;
import com.pig4cloud.pigx.contract.enums.ContractOrderEnum;
import com.pig4cloud.pigx.contract.enums.ContractPositionEnum;
import com.pig4cloud.pigx.contract.executer.order.OrderExecutor;
import com.pig4cloud.pigx.contract.mapper.ContractOrderHistoryMapper;
import com.pig4cloud.pigx.contract.mapper.ContractOrderMapper;
import com.pig4cloud.pigx.contract.mapstruct.ContractOrderHistoryStruct;
import com.pig4cloud.pigx.contract.mapstruct.ContractOrderStruct;
import com.pig4cloud.pigx.contract.mongo.entity.ContractSymbolMongo;
import com.pig4cloud.pigx.contract.open.api.dto.OpenApiOrderBookDTOV1;
import com.pig4cloud.pigx.contract.open.api.dto.OpenApiOrderDTOV1;
import com.pig4cloud.pigx.contract.open.api.vo.OpenApiOrderBookVOV1;
import com.pig4cloud.pigx.contract.open.api.vo.OpenApiOrderVOV1;
import com.pig4cloud.pigx.contract.utils.ContractUtils;
import com.pig4cloud.pigx.contract.vo.ContractOrderInitResVO;
import lombok.AllArgsConstructor;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 */
@Service
@AllArgsConstructor
public class OrderServiceImplV1 extends ServiceImpl<ContractOrderMapper, ContractOrder> implements OrderServiceV1 {

    private final OpenApiContractUtilsV1 apiContractUtils;
    private final ContractOrderHistoryMapper contractOrderHistoryMapper;

    private final ContractUtils contractUtils;

    private final OrderExecutor orderExecutor;

    private final MongoTemplate mongo;

    /**
     * 获取用户当前挂单 - 币对
     *
     * @param symbolName 币对名称
     * @return 挂单list
     */
    @Override
    public R<List<OpenApiOrderVOV1>> getUserNowOrderBySymbol(String symbolName) {
        // 币对校验
        apiContractUtils.getSymbolMongo(symbolName);
        OpenApiOrderDTOV1 dto = new OpenApiOrderDTOV1();
        dto.setSymbolName(symbolName);
        List<OpenApiOrderVOV1> list = this.getOrderList(dto);
        return R.ok(list);
    }

    /**
     * 获取用户当前全部挂单
     *
     * @return 挂单list
     */
    @Override
    public R<List<OpenApiOrderVOV1>> getUserNowOrder() {
        List<OpenApiOrderVOV1> list = this.getOrderList(new OpenApiOrderDTOV1());
        return R.ok(list);
    }

    /**
     * 查询订单详情
     *
     * @param condition 查询条件
     * @return {@link OpenApiOrderVOV1}
     * @since 2023/1/9 19:19
     */
    @Override
    public OpenApiOrderVOV1 getOrder(ContractOrder condition) {
        PigxUser user = SecurityUtil.getLoginUserNotNull();

        OpenApiOrderVOV1 vo;
        // 查询当前订单
        QueryWrapper<ContractOrder> queryOrder = new QueryWrapper<>();
        queryOrder.lambda()
                .eq(ContractOrder::getUserId, user.getId())
                .eq(!Objects.isNull(condition.getId()), ContractOrder::getId, condition.getId())
                .eq(!Objects.isNull(condition.getClientOrderId()), ContractOrder::getClientOrderId, condition.getClientOrderId())
                .orderByDesc(ContractOrder::getCreateTime)
                .last("limit 1");
        ContractOrder order = baseMapper.selectOne(queryOrder);

        if (!Objects.isNull(order)) {
            vo = ContractOrderStruct.INSTANCE.entity2ApiVOV1(order);
            vo.setTime(order.getCreateTime().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());

        } else {
            // 为空，查询订单历史
            QueryWrapper<ContractOrderHistory> queryHistory = new QueryWrapper<>();
            queryHistory.lambda()
                    .eq(ContractOrder::getUserId, user.getId())
                    .eq(!Objects.isNull(condition.getId()), ContractOrderHistory::getId, condition.getId())
                    .eq(!Objects.isNull(condition.getClientOrderId()), ContractOrderHistory::getClientOrderId, condition.getClientOrderId())
                    .orderByDesc(ContractOrderHistory::getCreateTime)
                    .last("limit 1");
            ContractOrderHistory history = contractOrderHistoryMapper.selectOne(queryHistory);

            // 依然为空
            if (Objects.isNull(history)) {
                // 订单信息不存在
                throw new RuntimeException(MessageUtils.get(I18nMessages.ORDER_INFORMATION_COULD_NOT_BE_QUERIED));
            }

            vo = ContractOrderHistoryStruct.INSTANCE.history2ApiVOV1(history);
            vo.setTime(history.getCreateTime().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
        }

        vo.setSide(ContractOrderEnum.Side.findName(vo.getSide()));
        vo.setType(ContractOrderEnum.Type.findName(vo.getType()));
        vo.setPositionMode(ContractPositionEnum.Mode.findName(vo.getPositionMode()));
        vo.setStatus(ContractOrderEnum.Status.findName(vo.getStatus()));
        vo.setOperationType(ContractOrderEnum.OperationType.findName(vo.getOperationType()));
        contractUtils.setScaleBySymbol(vo, vo.getSymbolName());

        return vo;
    }

    private List<OpenApiOrderVOV1> getOrderList(OpenApiOrderDTOV1 dto) {
        PigxUser user = apiContractUtils.getUser();
        try {
            // 查询
            List<ContractOrder> orderList = baseMapper.selectList(new QueryWrapper<ContractOrder>() {{
                if (dto.getSymbolName() != null) {
                    eq("symbol_name", dto.getSymbolName());
                }
                if (dto.getOrderId() != null) {
                    eq("id", dto.getOrderId());
                }
                if (dto.getType() != null) {
                    eq("type", dto.getType());
                }
                eq("user_id", user.getId());
                orderByDesc("create_time");
            }});
            // 遍历类型转换 字典翻译 精度调整
            List<OpenApiOrderVOV1> voList = new ArrayList<>();
            orderList.forEach(e -> {
                OpenApiOrderVOV1 vo = new OpenApiOrderVOV1();
                BeanUtils.copyProperties(e, vo);
                vo.setTime(e.getCreateTime().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
                vo.setSide(ContractOrderEnum.Side.findName(vo.getSide()));
                vo.setType(ContractOrderEnum.Type.findName(vo.getType()));
                vo.setPositionMode(ContractPositionEnum.Mode.findName(vo.getPositionMode()));
                vo.setStatus(ContractOrderEnum.Status.findName(vo.getStatus()));
                vo.setOperationType(ContractOrderEnum.OperationType.findName(vo.getOperationType()));
                contractUtils.setScaleBySymbol(vo, e.getSymbolName());
                voList.add(vo);
            });
            return voList;
        } catch (Exception e) {
            throw OpenApiUtil.getError(AbnormalEnum.MSG.DISCONNECTED);
        }
    }

    /**
     * 撤销单个订单
     *
     * @param id id
     * @since 2022/8/25 11:26
     */
    @Override
    public R cancel(Long id) {
        // 获取用户信息
        PigxUser user = apiContractUtils.getUser();
        ContractOrderCancelDTO dto = new ContractOrderCancelDTO();
        dto.setId(id);
        dto.setUserId(user.getId());
        orderExecutor.cancel(dto);
        return R.ok();
    }

    /**
     * 撤销当前用户全部订单
     *
     * @since 2022/9/3 21:40
     */
    @Override
    public R cancelAll() {
        // 获取用户信息
        PigxUser user = apiContractUtils.getUser();
        orderExecutor.cancelByUser(user.getId());
        return R.ok();
    }

    /**
     * 开仓
     *
     * @param dto dto
     * @return {@link R< ContractOrderInitResVO>}
     * @since 2022/8/16 18:08
     */
    @Override
    public R<ContractOrderInitResVO> open(ContractOrderOpenDTO dto) {
        // 币对校验
        ContractSymbolMongo mongo = apiContractUtils.getSymbolMongo(dto.getSymbolName());
        dto.setSymbolId(mongo.getId());
        try {
            ContractOrderNewDTO newDTO = ContractOrderStruct.INSTANCE.openDTO2NewDTO(dto);
            PigxUser user = SecurityUtil.getLoginUserNotNull();
            newDTO.setUserId(user.getId());
            newDTO.setOperationType(ContractOrderEnum.OperationType.OPEN.getValue());
            newDTO.setIsRobot(CommonDictEnum.YesOrNo.NO.getValue());
            ContractOrderInitResVO res = orderExecutor.init(newDTO);

            return R.ok(res);
        } catch (Exception e) {
            throw OpenApiUtil.getError(AbnormalEnum.MSG.UNSUPPORTED_OPERATION, e.getMessage());
        }
    }

    /**
     * 平仓
     *
     * @param dto dto
     * @return {@link R< ContractOrderInitResVO>}
     * @since 2022/9/6 14:38
     */
    @Override
    public R<ContractOrderInitResVO> close(ContractOrderCloseDTO dto) {
        try {
            ContractOrderInitResVO res = orderExecutor.close(dto);
            return R.ok(res);
        } catch (Exception e) {
            throw OpenApiUtil.getError(AbnormalEnum.MSG.UNSUPPORTED_OPERATION, e.getMessage());
        }
    }

    /**
     * 市价全平
     *
     * @param dto dto
     * @return {@link ContractOrderInitResVO}
     * @since 2023/2/28 16:22
     */
    @Override
    public ContractOrderInitResVO closeMarket(ContractOrderCloseMarketDTO dto) {
        // 判断入参
        if (Objects.isNull(dto.getPositionId())
                && (Strings.isBlank(dto.getSymbolName())
                || Strings.isBlank(dto.getPositionMode())
                || Strings.isBlank(dto.getSide()))) {
            throw new RuntimeException(MessageUtils.get(I18nMessages.DATA_IS_EMPTY));
        }

        PigxUser user = SecurityUtil.getLoginUserNotNull();

        // 查询仓位信息
        ContractPosition condition = new ContractPosition();
        condition.setId(dto.getPositionId());
        // 确保是本人的仓位
        condition.setUserId(user.getId());
        condition.setSymbolName(dto.getSymbolName());
        condition.setMode(dto.getPositionMode());
        condition.setSide(dto.getSide());
        ContractPosition position = contractUtils.getOneCurrentPosition(condition, false);
        // 校验仓位信息
        ContractUtils.checkCurrentPosition(position, false);

        ContractOrderCloseByPositionDTO closeDTO = new ContractOrderCloseByPositionDTO();
        closeDTO.setPositionId(position.getId());
        closeDTO.setClientOrderId(dto.getClientOrderId());
        return orderExecutor.closeByPosition(closeDTO);
    }

    @Override
    public R<OpenApiOrderBookVOV1> getOrderBook(OpenApiOrderBookDTOV1 dto) {
        // 非空校验
        if (dto.getIsBuy() == null || "".equals(dto.getIsBuy())) {
            throw OpenApiUtil.getError(AbnormalEnum.MSG.MANDATORY_PARAM_EMPTY_OR_MALFORMED, "'isBye'");
        }
        if (dto.getSymbolName() == null || dto.getSymbolName().isEmpty()) {
            throw OpenApiUtil.getError(AbnormalEnum.MSG.MANDATORY_PARAM_EMPTY_OR_MALFORMED, "'symbolName'");
        }
        // 校验比对
        ContractSymbolMongo symbolMongo = apiContractUtils.getSymbolMongo(dto.getSymbolName());
        // 获取用户
        if (dto.getUserId() == null) {
            PigxUser user = apiContractUtils.getUser();
            dto.setUserId(user.getId());
        }
        try {
            String orderBookName = ContractUtils.getOpponentOrderBookName(dto.getIsBuy());
            Sort sort;
            // 买单
            if (Objects.equals(CommonDictEnum.YesOrNo.YES.getValue(), dto.getIsBuy())) {
                // 根据价格升序
                sort = Sort.by(Sort.Order.asc("price"));

            } else {
                // 卖单，根据价格降序
                sort = Sort.by(Sort.Order.desc("price"));
            }
            // 相同币对
            Criteria criteria = Criteria.where("symbolName").is(dto.getSymbolName())
                    // 未交易数量 > 0
                    .and("noDealNum").gt(0)
                    // 不是新订单
                    .and("isNew").is(false);

            // 设置额外筛选条件
            criteria.and("userId").ne(dto.getUserId())
                    // 不能是机器人的订单
                    .and("isRobot").is(CommonDictEnum.YesOrNo.NO.getValue());
            // 如果是买单，寻找价格低的订单薄
            if (dto.getPrice() == null) {
                dto.setPrice(symbolMongo.getIndexPrice());
            }
            if (Objects.equals(CommonDictEnum.YesOrNo.YES.getValue(), dto.getIsBuy())) {
                criteria.and("price").lte(dto.getPrice());
            } else {
                // 如果是卖单，寻找价格高的订单薄
                criteria.and("price").gte(dto.getPrice());
            }
            // 查询最优订单
            Query query = Query.query(criteria)
                    .with(sort)
                    .limit(1);

            // 查询
            OpenApiOrderBookVOV1 one = mongo.findOne(query, OpenApiOrderBookVOV1.class, orderBookName);
            if (one != null && one.getPrice() == null) {
                one.setPrice(symbolMongo.getIndexPrice());
                contractUtils.setScaleBySymbol(one, one.getSymbolName());
            }
            return R.ok(one);
        } catch (Exception e) {
            throw OpenApiUtil.getError(AbnormalEnum.MSG.DISCONNECTED);
        }
    }

}
